home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / linuxcon.000 / linuxcon / linuxconf-1.6 / translate / translat.doc < prev    next >
Text File  |  1996-04-14  |  17KB  |  443 lines

  1.   Translation system for Linuxconf
  2.   Introduction
  3.  
  4.   Linuxconf is a large software component, full of menus, and dialogs.
  5.   To be easily translatable, all messages must be extracted from the C++
  6.   source code and place into dictionnaries which can be translated effi-
  7.   ciently.  A special set of tools has been designed to achieve this.
  8.   They are described here.
  9.  
  10.   1.  Introduction
  11.  
  12.   This document describes both how the system works and how translators
  13.   can use it. It starts by explaining how programmers can use it to
  14.   produce translatable programs. The section "how to translate" explains
  15.   how translators can use this system to translate linuxconf or any
  16.   programs written using this system.
  17.  
  18.   2.  Principles
  19.  
  20.   To make programs easily translatable, all messages should be placed in
  21.   dictionnaries. A dictionnary is made of message entries. Each message
  22.   has a unique ID and a value. In the C++ source, programmers are
  23.   refering to those messages using the ID whenever they want to print or
  24.   say something.
  25.  
  26.   Each time a programmer need a new message, he has to add it in the
  27.   message dictionnary and reference it from the C++ source code. This is
  28.   how most system works (There are other translation system out there).
  29.  
  30.   The system used by Linuxconf is basically different. Messages are
  31.   defined in the C++ source code and the dictionnaries are built by
  32.   scanning all C++ source files. Messages are defined in the C++ code.
  33.   Programmers must provide and ID and a value for each message right in
  34.   the source code. This is much easier (or nicer) to do this right in
  35.   the source code than to go back and forth in the dictionnary.
  36.   Furthermore, the programmer directly see the message definition in the
  37.   source. With other system, only the message ID is visible in the
  38.   source.
  39.  
  40.   Using the magic of the C preprocessor, the message value is not
  41.   compiled in the object code at all. Seen this way, the translation
  42.   system used by Linuxconf yield the same result as other system.  It is
  43.   just nicer to use for programmers.
  44.  
  45.   Lets describe how a programmer use the system.
  46.  
  47.   2.1.  One dictionnary per source directory
  48.  
  49.   It is best to define one message dictionnary per sub-project or sub-
  50.   directory. This is easier to manage and avoid ID name space
  51.   congestion. For each directory source of Linuxconf you have one "dic"
  52.   file and one "m" file. Both file are produced simply by doing
  53.  
  54.                make msg
  55.  
  56.   This command scans all C++ source file of the current directory and
  57.   update the file ../messages/sources/DIRECTORY.dic and the file
  58.   DIRECTORY.m, where DIRECTORY is the name of the current directory.
  59.  
  60.   make msg use the ../translate/msgscan utility to scan the source. This
  61.   utility looks for specific constructs in the C++ source file. Here
  62.   they are.
  63.  
  64.   2.2.  The MSG_U macro
  65.  
  66.   The MSG_U macro defines a new message. It defines both its ID and its
  67.   value. This macro is usable anywhere a C++ string would be.
  68.  
  69.                #include "prjfoo.m"
  70.  
  71.                int foo()
  72.                {
  73.                        printf (MSG_U(M_MSG1,"Entering function foo"));
  74.                }
  75.  
  76.   MSG_U defines a single value. U stands for unilingual. It only defines
  77.   one value.
  78.  
  79.   2.3.  The MSG_B macro
  80.  
  81.   The MSG_B macro is like the MSG_U macro, except it defines two values,
  82.   allowing a programmer to code immediatly two languages at once. The B
  83.   stands for bilingual. This has not been used in the Linuxconf project
  84.   but has proven effective for other projects.
  85.  
  86.                #include "prjfoo.m"
  87.  
  88.                int foo()
  89.                {
  90.                        printf (MSG_U(M_MSG1
  91.                                ,"Entering function foo\n"));
  92.                                ,"DΘmarrage de la fonction foo\n"));
  93.                }
  94.  
  95.   2.4.  The MSG_R macro
  96.  
  97.   The MSG_R macro simply references an already defined message. This
  98.   message may have been defined in another source file (of the same
  99.   project). Like the other macros, MSG_R may be used anywhere a C++
  100.   string is.
  101.  
  102.   2.5.  The MSG_VERSION macro
  103.  
  104.   This macro has not been used so far. It would allow one programmer to
  105.   raise the version number of a dictionnary, preventing older
  106.   application to use the newer potentially incompatible dictionnary.
  107.  
  108.   The msgclean utility also plays with the version number of the
  109.   dictionnary. The MSG_VERSION macro is still a concept rather than a
  110.   useful addition. Stay tune...
  111.  
  112.   2.6.  The magic of the MSG_ macros
  113.  
  114.   The MSG_ macros perform two tasks. First, they are easily spotted by
  115.   the msgscan utility. The parsing is simple and reliable even if the
  116.   C++ source code is not functionnal. Second, they hide the retrieval
  117.   mecanism (How the message value is retrieved from the binary
  118.   dictionnary at runtime).
  119.  
  120.   The msgscan utility produce the .m file which looks like this for the
  121.   simple example above.
  122.  
  123.                FILE prjfoo.m:
  124.  
  125.                extern const char **_dictionnary_prjfoo;
  126.                #ifndef DICTIONNARY_REQUEST
  127.                        #define DICTIONNARY_REQUEST \
  128.                        const char **_dictionnary_prjfoo;\
  129.                        TRANSLATE_SYSTEM_REQ _dictionnary_req_prjfoo\
  130.                                ("prjfoo",_dictionnary_prjfoo,55,1);\
  131.                        void dummy_dict_prjfoo(){}
  132.                #endif
  133.                #ifndef MSG_U
  134.                        #define MSG_U(id,m)     id
  135.                        #define MSG_B(id,m,n)   id
  136.                        #define MSG_R(id)       id
  137.                #endif
  138.                #define M_MSG1  _dictionnary_prjfoo[0]
  139.  
  140.   As you see, one global variable is created: _dictionnary_prjfoo.  A
  141.   special macro DICTIONNARY_REQUEST is defined. This macro should be
  142.   placed in one source of the project. It is generally place in the file
  143.   _dict.c presented later.
  144.  
  145.   3.  How to use it
  146.  
  147.   To produce a translatable program, do the following
  148.  
  149.   ╖  Replace all string message with MSG_U or MSG_B macros, giving each
  150.      message a unique ID.
  151.  
  152.   ╖  include (#include) the .m file in each source file using the MSG_x
  153.      macros. This file is generally named directory.m where directory is
  154.      the name of the current directory.
  155.  
  156.   ╖  Create a file _dict.c. The content of this file is shown below.
  157.  
  158.   ╖  Use "make msg" to extract the messages. This produces/updates the
  159.      dictionnary file directory.dic and produces the include file
  160.      directory.m.
  161.  
  162.   ╖  Compile and link your program.
  163.  
  164.   ╖  Use "make msg.eng" to produce the english binary dictionnary.  The
  165.      file produced should be placed where your program expects it.
  166.  
  167.   We will now describe further the different steps involved.
  168.  
  169.   3.1.  The make msg command and msgscan utility
  170.  
  171.   The make msg command invokes the msgscan utility. This utility scan a
  172.   set of C or C++ source file, updates a dictionnary file and produces
  173.   one include file.
  174.  
  175.   Here is the command use to update the dictionnary of the sub-project
  176.   uucp of the Linuxconf project.
  177.  
  178.                        ../translate/msgscan uucp \
  179.                                ../messages/sources/uucp.dic uucp.m EF *.c
  180.  
  181.   The first argument is the name of the dictionnary. The second argument
  182.   is the path of the dictionnary file. As you see, dictionnary file are
  183.   kept in a single directory for all projects. They are seldom. This
  184.   eases the works of translators. The third argument is the path of the
  185.   include file, which is produced in the current directory.
  186.  
  187.   The fourth argument is the letter tags used to identify messages
  188.   defined with the macro MSG_U and MSG_B. Messages defined with MSG_U
  189.   will be tagged with the letter E (English) and messages defined with
  190.   MSG_B will be tagged with E for the first value and F (French) for the
  191.   second.
  192.  
  193.   3.2.  The _dict.c file
  194.  
  195.   It is good pratice to place the DICTIONNARY_REQUEST macro in a file
  196.   _dict.c. There is generally one such a file per directory. Its
  197.   contents is generally:
  198.  
  199.                #include "this_directory.m"
  200.                #include <translat.h>
  201.                DICTIONNARY_REQUEST
  202.  
  203.   At least this dependancy should be placed in your makefile
  204.  
  205.           _dict.o: _dict.c this_directory.m
  206.  
  207.   This will ensure that each time you update your dictionnary (and the m
  208.   header file), _dict.c will be recompile, ensuring proper recording of
  209.   the dictionnary revision and number of message.  This will avoid
  210.   executing a program with an obsolete or incompatible binary
  211.   dictionnary.
  212.  
  213.   Given that _dict.c is small, the compilation is pretty short.
  214.  
  215.   3.3.  The msgcomp utility
  216.  
  217.   Once you have compiled and linked your program, you must "compiled"
  218.   all the dictionnaries used in your program into one binary
  219.   dictionnary.  This is done by the msgcomp utility. Here is the command
  220.   used when doing "make msg.eng" for the Linuxconf project.  This
  221.   produces the english binary dictionnary.
  222.  
  223.            ../translate/msgcomp -p../messages/sources/ \
  224.                /tmp/linuxconf-msg-1.3.eng eE \
  225.                askrunlevel dialog dnsconf fstab \
  226.                misc main netconf mailconf uucp userconf
  227.  
  228.   This commands take all dictionnaries for sub-projects askrunlevel
  229.   dialog dnsconf fstab misc main netconf mailconf uucp and userconf and
  230.   produce a single binary dictionnary.
  231.  
  232.   The -p option tells msgcomp to look for those dic files (
  233.   askrunlevel.dic dialog.dic ...)  in the directory
  234.   ../messages/sources/.
  235.  
  236.   The argument /tmp/linuxconf-msg-1.3.eng is the file to produce.  The
  237.   argument eE instructs msgcomp to extract message'values with the 'e'
  238.   tag. If there is no such value for a given message, the value with the
  239.   'E' tag will be used.
  240.  
  241.   3.3.1.  Convention used for tags
  242.  
  243.   Dictionnary file contain the definition for all messages. Each
  244.   messages may have different values, identified by a tag letter. When
  245.   messages are extracted by msgscan, it is instructed to associate
  246.   values with given tags. By convention, we use upper case letter to
  247.   identify message's value extracted from the source code. Lower case
  248.   value are used by translators.
  249.  
  250.   We assume here that programmers are bad writters. We let them give
  251.   their best shots for messages and we are allowed to override their
  252.   work without overwriting it. By giving precedence to 'e' tags over 'E'
  253.   we are saying that translators work override the work of programmers,
  254.   but we are not forcing the translators to rewrite everything.
  255.  
  256.   3.4.  The msgclean utility
  257.  
  258.   The msgscan utility maintains dictionnary. At some point some messages
  259.   may become obsolete (Unused in any source files). The msgclean is used
  260.   to clean messages without values in the dic file.
  261.  
  262.   For the Linuxconf project, the make target msg.clean is defined for
  263.   that purpose.
  264.  
  265.   Be aware that applying msgclean on a dictionnary file with obsolete
  266.   message has an important side effect. Some message being deleted, the
  267.   numbering of all following message will be changed. All source using
  268.   the m include file should be recompiled.
  269.  
  270.   To avoid problems, the msgclean utility automaticly increases the
  271.   revision number of the dictionnary. This prevents using a dictionnary
  272.   with an incompatible program.
  273.  
  274.   4.  Usage restriction
  275.  
  276.   The stategy used is mainly targetted at C++ code. With some
  277.   restriction, it may be used for C code. Here are the main feature that
  278.   probably don't work with C.
  279.  
  280.      static initialisation
  281.         In C++ one can write the following code.
  282.  
  283.                           static char *tb[]={
  284.                                   foo(1),foo(22)
  285.                           };
  286.  
  287.      where foo is a function. The C++ compiler will generate the proper
  288.      code which will be probably called once. The MSG_U macro (and
  289.      others) are not hiding function call, but are indeed dynamic in
  290.      some sens. C does not support this. Other translation strategy
  291.      based on dictionnary do have the same limitation though.
  292.  
  293.   The exemple using the static char *tb[] is also causing a problem in
  294.   C++ if the variable is declared outside of a function. The problem
  295.   appear because the "hidden" initialisation code generated by the
  296.   compiler is called very early, often before main() is called.
  297.   Normally, the function translat_load() which bring the dictionnary in
  298.   memory is called by main().
  299.  
  300.   Fortunatly, the current implementation, where _dictionnary_system is a
  301.   pointer will trigger a seg fault whenever this condition is met. This
  302.   fault will be trigger all the time, because all initialisation are
  303.   called before main. The strategy is safe.
  304.  
  305.   5.  Recommend usage and convention
  306.  
  307.   5.1.  Naming convention for message's ID
  308.  
  309.   To help peoples who will translat your Linuxconf, I have used a
  310.   convention for the ID's name.
  311.  
  312.      B_ Buttons.
  313.  
  314.      E_ Error message start with this.
  315.  
  316.      F_ Field labels start with this.
  317.  
  318.      I_ Dialog instroduction start with this.
  319.  
  320.      M_ All menu entries start with this prefix.
  321.  
  322.      N_ Notices and warning start with this.
  323.  
  324.      P_ When the user is prompted for a password, the message's ID start
  325.         with this.
  326.  
  327.      Q_ Identify a question (Generally a Yes/No prompt).
  328.  
  329.      T_ Dialog's title start with this.
  330.  
  331.      X_ All other messages which fit in no category.
  332.  
  333.   6.  How to translate
  334.  
  335.   6.1.  Go simple
  336.  
  337.   One way to translate is to go right in the .dic files and add
  338.   translations for each message using a different tag. Then use the
  339.   msgcomp utility to extract the proper definition.
  340.  
  341.   At first, there is little problem doing this. The msgscan utility
  342.   read,update and save the .dic file, so your changes won't be lost.
  343.  
  344.   The problem come from the way software is developped. First we develop
  345.   and then, when it is stable, we translate. Doing so mean that we have
  346.   to walk all the .dic files to make sure our translation still fit with
  347.   the original messages (English version for example). Those original
  348.   messages may have changed.
  349.  
  350.   A different scheme was choosen for Linuxconf.
  351.  
  352.   6.2.  Organisation of the messages directory
  353.  
  354.   The messages directory contain one subdirectory per language plus one
  355.   sources directory. This directory contains all the These file are
  356.   never hand edited.
  357.  
  358.   Each other directory has a copy of those .dic files with the proper
  359.   translation. A special utility msgupd has been created: it basicly
  360.   compared all messages in the sources directory with messages in the
  361.   translated directory. It compare only one language (say the english
  362.   version).
  363.  
  364.   Mostly, msgupd will tell you
  365.  
  366.   ╖  Which messages are new.
  367.  
  368.   ╖  Which messages have changed (The english wording).
  369.  
  370.   Using that information, you know exactly what you have to do to keep
  371.   your work in sync with the current release of Linuxconf.  msgupd will
  372.   reorder the translated .dic file (Not the one in the sources
  373.   directory) so all messages which needed work are at the beginning of
  374.   the file. It also add a comment (.dic files may have comments like
  375.   most normal Unix configuration file) explaining what have to be done.
  376.  
  377.   If the english version of the message was changed, it will retag the
  378.   version in the translated file and add the new version, plus a
  379.   comment.  The old english message will have the tag "Z". You can see
  380.   easily what is the change.
  381.  
  382.   6.3.  The msgupd utility
  383.  
  384.   The file rules.mak shows the rules for one translation (which is not
  385.   done yet). Look for the target msg.cfr and upd.cfr.  To add a new
  386.   language, do this
  387.  
  388.   ╖  Create a new directory empty in the messages directory, for
  389.      example, mar for Alien language.
  390.  
  391.   ╖  Customise rules.mak and add the target msg.mar and upd.mar.
  392.  
  393.   ╖  Run the following command. This will fill the messages/mar
  394.      directory with all the necessary .dic files.
  395.  
  396.                        make upd.mar
  397.  
  398.   ╖  Go into messages/mar and edit each .dic file and add the proper
  399.      translation as needed.
  400.  
  401.   ╖  Run the following command to produce the binary dictionnary
  402.      required to run Linuxconf.
  403.  
  404.                        make msg.mar
  405.  
  406.   ╖  Set the following environnement variable and run Linuxconf.
  407.  
  408.   ╖  export LINUXCONF_LANG=mar
  409.  
  410.   ╖  export LINUXCONF_DICT=/tmp
  411.  
  412.      This variable is optionnal. Linuxconf will normally look for its
  413.      message dictionnary in /usr/lib/linuxconf. This variable override
  414.      this.  The msg.* makefile's target generally produce their output
  415.      in /tmp. This is useful to test new messages without breaking the
  416.      current installation of Linuxconf.
  417.  
  418.      Be aware that this mecanism only work if you execute Linuxconf as
  419.      root. For security reason, a normal user can't override the message
  420.      dictionnary of Linuxconf (Although he can select a different
  421.      language from /usr/lib/linuxconf if available).
  422.  
  423.   6.4.  The msgcomp utility
  424.  
  425.   The msgcomp utility has been tweaked to support the distribute
  426.   directory concept. Mainly it use the .dic file in the sources
  427.   directory as a reference. Message number ID are defined from this
  428.   file. It then used (optionnally) alternative
  429.  
  430.   7.  Licensing
  431.  
  432.   The translate directory is part of the Linuxconf project but carry a
  433.   special license. There is no resctriction on usage. Feel free to
  434.   incorporate this system to any project.
  435.  
  436.   This simple license does not apply to the rest of Linuxconf which is
  437.   covered by the standard GNU Copyleft license. See the file LICENSE in
  438.   the root directory.
  439.  
  440.   If you find it useful for other project, send me a note and some
  441.   comments if possible.
  442.  
  443.